iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
Vue.js

淺談vue3源碼,很淺的那種系列 第 26

[Day 25]ast抽象語法樹 - 2——解析標籤

  • 分享至 

  • xImage
  •  

或許有人會想說我這兩天一直在鐵人賽跟離職的學姊示好,就不怕被公司的人看到嗎?哎這我還真不怕,畢竟咱公司紀律嚴謹,上班時間逛不了論壇,下了班就該好好休息哪還看我的廢文,沒在怕ㄏ

所以開始遍歷template吧。在/src/ast/parse.ts的parse方法體加上以下代碼:

while (index < template.length - 1) {
  const rest = template.substring(index);
  // 檢測剩餘文字「開頭」為開始標籤、結束標籤或內文
  if (startRegExp.test(rest)) {

  } else if (endRegExp.test(rest)) {

  } else if (wordRegExp.test(rest)) {

  } else {
    index++;
  }
}

很簡單的邏輯,遍歷template的每個字,看以那個字開頭的是上標籤、下標籤還是內文或啥也不是。除了rest是剩餘的英文以外,其他都沒甚麼好說的。

還記得昨天的流程嗎?不記得就複習一下:

  1. 每當檢測到上標籤,就給兩個棧分別入棧,並把標籤種類放進textStack棧頂的tag屬性。
  2. 檢測到屬性,就放到textStack棧頂的props屬性。
  3. 發現標籤裡面還有標籤,就重複1.的步驟。
  4. 看到標籤內文就push進textStack棧頂的children屬性。
  5. 看到下標籤代表解析完棧頂的標籤,就讓兩個棧頂出棧,同時把textStack出棧的那項push進新棧頂的children屬性。

當startRegExp.test(rest)為true時,代表我們遍歷到了上標籤,這時就要給tagStack和textStack入棧。

if (startRegExp.test(rest)) {
  // 捕獲標籤內文字賦值tag
  const tag = rest.match(startRegExp)[1];
  const attrsStr = rest.match(startRegExp)[2];
  const attrs = parseAttrsString(attrsStr);

  tagStack.push(tag);
  textStack.push({ tag, children: [], attrs });
  // 標籤文字字數 + 2即為開始標籤字數,將指針後移至開始標籤之後
  index += tag.length + 2 + (attrsStr ? attrsStr.length : 0);
}

parseAttrsString之後再說,先假裝這裡有個能把標籤屬性從字串解析成對象的函數。所以這邊做的事就是把標籤名和屬性解析出來,然後入棧。

至於最下面那段:

index += tag.length + 2 + (attrsStr ? attrsStr.length : 0);

把上標籤都遍歷完了,就把指針移動到上標籤的後面,之所以+2是因為上標籤除了裡面的文字與空格,外面還有兩個尖括號。

接著討論endRegExp.test(rest)為true,也就是匹配到下標籤的情況。

else if (endRegExp.test(rest)) {
  const tag = rest.match(endRegExp)[1];
  if (tag === tagStack[tagStack.length - 1]) {
    tagStack.pop();
    const popText = textStack.pop();
    textStack[textStack.length - 1].children.push(popText);
  } else {
    throw new Error(tagStack[tagStack.length - 1] + '標籤未封閉!');
  }
  index += tag.length + 3;
}

看完下標籤,就代表當前這個棧頂的標籤已經解析完成,就能讓兩個棧都出棧,並把解析完的textStack出棧的項push進新棧頂的children屬性。

最下面index之所以是+3是因為下標籤有'</'和'>'。

接著看wordRegExp.test(rest)為true,也就是匹配到內文時。

else if (wordRegExp.test(rest)) {
  // 剩餘文字開頭非標籤,結尾為下標籤,意即標籤內容
  const text = rest.match(wordRegExp)[1];
  // 檢查標籤內文字是否全為空
  if (!/^\s+$/.test(text)) {
    textStack[textStack.length - 1].children = parseTextData(text);
  }
  index += text.length;
}

parseTextData同樣先不管它,就當作它是一個能解析內文,把標籤中的內容變成純文字的方法,之後再回來補。

如此一來解析內文的部分就單純了許多,把內文賦值給textStack棧頂的children屬性,然後讓指針移至內文後面。

順帶一提,這邊可以不必擔心內文會不會包含更多html子標籤,因為如果有子標籤,就會被前面的if(startRegExp.test(rest))判斷攔截,能進到else if (wordRegExp.test(rest))就代表解析的過程不會遇到更多子標籤了。

最後迴圈解析template,看到上標籤就解析其屬性,給tagStack和textStack入棧;看到下標籤就雙棧出棧,把textStack出棧項推入textStack棧頂的children;看到內文就解析內文,賦值給textStack棧頂的children……不斷重複。

然後就能完成這個流程圖:

大致上的邏輯就是這樣,接下來就剩下如何解析屬性以及內文,明天我們將著手開始進行這兩項解析。

github - [Day 25]ast抽象語法樹 - 2——解析標籤


上一篇
[Day 24]ast抽象語法樹 - 2——前置準備及明確目標
下一篇
[Day 26]ast抽象語法樹 - 3——解析文字
系列文
淺談vue3源碼,很淺的那種31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言